package com.hutu.service.front;

import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.hutu.core.constant.RedisKeyConstants;
import com.hutu.core.constant.RoleConstants;
import com.hutu.core.enums.front.DeletedEnum;
import com.hutu.core.enums.front.LoginTypeEnum;
import com.hutu.core.enums.front.ResponseCodeEnum;
import com.hutu.core.enums.front.StatusEnum;
import com.hutu.core.exception.BizException;
import com.hutu.mapper.biz.front.UserDOMapper;
import com.hutu.mapper.biz.front.UserRoleDOMapper;
import com.hutu.model.front.UserDO;
import com.hutu.model.front.UserRoleDO;
import com.hutu.model.front.common.Response;
import com.hutu.model.req.UserLoginReqVO;
import com.util.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;

/**
 * @author Jack
 * @since 2025-03-20 15:34
 */
@Service
@Slf4j
public class UserService extends ServiceImpl<UserDOMapper, UserDO> {

    @Resource
    private UserDOMapper userDOMapper;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Resource
    private UserRoleDOMapper userRoleDOMapper;

    @Resource
    private TransactionTemplate transactionTemplate;

    /**
     * 登录与注册
     *
     * @param userLoginReqVO
     * @return
     */
    public Response<?> loginAndRegister(UserLoginReqVO userLoginReqVO) {
        Integer type = userLoginReqVO.getType();

        LoginTypeEnum loginTypeEnum = LoginTypeEnum.valueOf(type);

        Long userId = null;

        // 获取柯里化函数并执行
        Function<LoginTypeEnum, Function<UserLoginReqVO, Response<?>>> loginLogic = getLoginLogic();

        //使用编程式事务
        return transactionTemplate.execute(status -> {
            try {
                // 业务逻辑代码
                 return loginLogic.apply(loginTypeEnum).apply(userLoginReqVO);

            } catch (Exception ex) {
                status.setRollbackOnly(); // 标记事务为回滚
                log.error("事务回滚");
                throw ex; // 重新抛出异常
            }

        });

    }

    // 定义处理不同登录类型的独立方法
    private Response<?> loginByPhone(UserLoginReqVO userLoginReqVO) {
        String verificationCode = userLoginReqVO.getCode();
        String phone = userLoginReqVO.getPhone();
        // 校验入参验证码是否为空
        Preconditions.checkArgument(StringUtils.isNotBlank(verificationCode), "验证码不能为空");

        // 构建验证码 Redis Key
        String key = RedisKeyConstants.buildVerificationCodeKey(phone);
        // 查询存储在 Redis 中该用户的登录验证码
        String sentCode = (String) redisTemplate.opsForValue().get(key);

        // 判断用户提交的验证码，与 Redis 中的验证码是否一致
        if (!StringUtils.equals(verificationCode, sentCode)) {
            throw new BizException(ResponseCodeEnum.VERIFICATION_CODE_ERROR);
        }
        // 通过手机号查询记录
        UserDO userDO = userDOMapper.selectByPhone(phone);

        log.info("==> 用户是否注册, phone: {}, userDO: {}", phone, JsonUtils.toJsonString(userDO));

        // 判断是否注册
        Long userId = null;
        if (Objects.isNull(userDO)) {
            // 若此用户还没有注册，系统自动注册该用户
            userId = registerUser(phone);

        } else {
            // 已注册，则获取其用户 ID
            userId = userDO.getId();
        }

        // SaToken 登录用户, 入参为用户 ID
        StpUtil.login(userId);

        // 获取 Token 令牌
        SaTokenInfo tokenInfo = StpUtil.getTokenInfo();

        // 返回 Token 令牌
        return Response.success(tokenInfo.tokenValue);

    }

    private Long registerUser(String phone) {
        // 获取全局自增的 ID
        Long globalId = redisTemplate.opsForValue().increment(RedisKeyConstants.MGUI_ID_GENERATOR_KEY);
        Date date = new Date();
        UserDO userDO = UserDO.builder()
                .phone(phone)
                .userId(String.valueOf(globalId)) // 自动生成全局 ID
                .nickname("用户_" + globalId) // 自动生成昵称, 如：用户_10000
                .status(StatusEnum.ENABLE.getValue()) // 状态为启用
                .createTime(date)
                .updateTime(date)
                .isDeleted(DeletedEnum.NO.getValue()) // 逻辑删除
                .build();
        // 添加入库
        userDOMapper.insert(userDO);
        // 获取刚刚添加入库的用户 ID
        Long userId = userDO.getId();

        // 给该用户分配一个默认角色
        UserRoleDO userRoleDO = UserRoleDO.builder()
                .userId(userId)
                .roleId(RoleConstants.COMMON_USER_ROLE_ID)
                .createTime(date)
                .updateTime(date)
                .isDeleted(DeletedEnum.NO.getValue())
                .build();
        userRoleDOMapper.insert(userRoleDO);

        // 将该用户的角色 ID 存入 Redis 中
        List<Long> roles = Lists.newArrayList();
        roles.add(RoleConstants.COMMON_USER_ROLE_ID);
        String userRolesKey = RedisKeyConstants.buildUserRoleKey(phone);
        redisTemplate.opsForValue().set(userRolesKey, JsonUtils.toJsonString(roles));

        return userId;

    }

    //TODO
    private Response<?> loginByPassword(UserLoginReqVO userLoginReqVO) {
        return Response.success();
    }

    // 柯里化函数，将登录类型映射到处理函数
    private Function<LoginTypeEnum, Function<UserLoginReqVO, Response<?>>> getLoginLogic() {
        return loginType -> {
            switch (loginType) {
                case VERIFICATION_CODE:
                    return this::loginByPhone;
                case PASSWORD:
                    return this::loginByPassword;
                default:
                    throw new IllegalArgumentException("Unsupported login type");
            }
        };
    }
}
